package cn.good.yan.basics.t4;

import cn.good.yan.basics.t2.mapper.BillMapper;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.*;

/**
 * 测试 Junit5
 *      常用注解：
 *       @BeforeEach()  每个测试方法 - 都需要执行    方法之前执行
 *       @AfterEach()   每个测试方法 - 都需要执行    方法之后执行
 *       @BeforeAll()   全部的测试方法只执行1次      方法之前执行
 *       @AfterAll()    全部的测试方法只执行1次      方法之后执行
 *       @Timeout(value = 500, unit = TimeUnit.MILLISECONDS)  超时时间 （如果超时，会自动报出超时异常） MILLISECONDS 毫秒
 *       @Disabled()    测试方法不执行
 *       @RepeatedTest(5) 重试测试，重试5次，该方法执行5次
 *  断言：测试方法需要满足的条件进行验证，检查方法是否合理
 *       Assertions 这个方法下是断言
 *  前置条件：测试方法需要满足的条件进行验证，前置条件使测试方法的执行终止
 *       Assumptions 前置条件
 *  参数化测试：可设置不同的参数多次运行某个测试方法
 *       @ValueSource(ints = {1,3,4,5})  指定基本参数值
 *       @MethodSource("stringStream")   指定自定义参数值 - 需要Stream包装
 * @author shengren.yan
 * @create 2023-02-13
 */
@DisplayName("A类的功能测试")
@SpringBootTest
public class test4 {

    @Autowired(required = false)
    private BillMapper billMapper;

    // 测试类 - 也可以加事务注解，（测试完自动回滚）
    @DisplayName("测试方法名称1")
    @Transactional(isolation = Isolation.DEFAULT)
    @Test
    void test1() {
        System.out.println("test1测试");
//        Bill bill = new Bill();
//        bill.setBillCode("sssss");
//        billMapper.add(bill);  // 新增 (会返回主键id)
    }

    // 重复测试 5 次
    @RepeatedTest(5)
    // 超时时间 （如果超时，会自动报出超时异常）
    @Timeout(value = 500, unit = TimeUnit.MILLISECONDS)
    // 表示这个注解不执行
    @Disabled
    @Test
    void test2() {
        System.out.println("test2测试");
    }

    // 每个测试方法 - 都需要执行
    @BeforeEach()
    void befor() {
        System.out.println("测试方法之前执行。。");
    }

    @AfterEach()
    void after() {
        System.out.println("测试方法之后执行。。");
    }

    @BeforeAll()
    static void beforAll() {
        System.out.println("所有的测试方法之前执行。。只执行1一次");
    }

    @AfterAll()
    static void afterAll() {
        System.out.println("所有的测试方法之后执行。。只执行1一次");
    }


    // 断言：测试方法需要满足的条件进行验证，检查方法是否合理
    @DisplayName("测试断言1 - 普通断言")
    @Test
    void testdy1() {
        System.out.println("test2测试");
        // assertEquals 是否相等  参数1 期望值 ，参数2 实际值
        assertEquals(1, 1, "测试断言1?");
        // 例子： assertEquals(期望值, 实际值, "测试断言1?");

        // assertSame 是否同同一个对象
//        assertSame(new User(), new User(), "测试断言2?");

        // assertArrayEquals 是否同数组相同
        assertArrayEquals(new int[]{1, 2}, new int[]{1, 2}, "测试断言3?");

        // assertThrows 异常断言，如果出异常代表正常
        assertThrows(ArithmeticException.class, () -> {
            System.out.println("执行业务代码逻辑。。。");
            int i = 10 / 0; // 会出现 ArithmeticException 异常
        }, "测试断言4?");

//        // assertTimeout 超时测试，如果超过设置的时间（参数1），就会出现异常
//        assertTimeout(Duration.ofMillis(1000), () -> {
//            System.out.println("执行业务代码逻辑--超时。。。");
//            Thread.sleep(1200);
//        }, "测试断言5?");

        // fail 快速失败 (满足条件就触发)
        if (1 == 1) {
            fail("测试断言6?");
        }

    }


    // 前置条件：测试方法需要满足的条件进行验证，前置条件使测试方法的执行终止
    @DisplayName("测试前置条件1 - 假设")
    @Test
    void testjs1() {
        Assumptions.assumeTrue(false,"结果不是true，才打印这个");
        System.out.println("jjj");
    }

    // 参数化测试：可设置不同的参数多次运行某个测试方法
    @DisplayName("参数化测试1 - 参数化")
    @ParameterizedTest
    @ValueSource(ints = {1,3,4,5})
    void testcs1(int i) {
        System.out.println(i);
        // 该方法 执行 4次，参数是：1,3,4,5
    }

    @DisplayName("参数化测试2 - 参数化")
    @ParameterizedTest
    // 参数化 指定具体方法名，自定义参数对象
    @MethodSource("stringStream")
    void testcs2(String str) {
        // 该方法 执行 3次，参数是："参数1","参数2","参数3"
        System.out.println(str);
    }

    // 方法名 stringStream
    static Stream<String> stringStream(){
        return Stream.of("参数1","参数2","参数3");
    }

}
